package exp3;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Calculator {
    //用于判断是不是运算符
    public static boolean isOperator(String str) {//字符串的比较用equals
        return "(".equals(str) || ")".equals(str) || "+".equals(str) || "-".equals(str) || "*".equals(str) || "/".equals(str);
    }

    private static int ADD = 1;
    private static int SUB = 1;
    private static int MUL = 2;
    private static int DIV = 2;

    //获取运算符优先级
    private static int getPriority(String str) {
        int result = 0;
        switch (str) {
            case "+":
                result = ADD;
                break;
            case "-":
                result = SUB;
                break;
            case "*":
                result = MUL;
                break;
            case "/":
                result = DIV;
                break;
            default:
                try {
                    throw new RuntimeException("输入运算符错误");
                } catch (RuntimeException e) {
                    e.printStackTrace();
                }
        }
        return result;
    }


    //计算方法，传入两个数据和运算符，可以得到相应的结果
    static double operation(double num1, double num2, String str) {
        double result = 0;
        switch (str) {
            case "+":
                result = num1 + num2;
                break;
            case "-":
                result = num2 - num1;
                break;
            case "*":
                result = num2 * num1;
                break;
            case "/":
                result = num2 / num1;
                break;
            default:
                try {
                    throw new RuntimeException("运算符输入错误");
                } catch (RuntimeException e) {
                    e.printStackTrace();
                }
        }
        return result;
    }

    //将expression存到ArrayList中
    private static List<String> arraysOrder(String expression) {
        StringBuilder str = new StringBuilder();        //用于组合数字
        int index = 0;            //用于标记扫描到表达式的哪个位置，也就是指向表达式的指针
        String ch = "";        //用于接收扫描到的字符
        ArrayList<String> list = new ArrayList<String>();          //创建一个list
        do {
            ch = expression.substring(index, index + 1);   //从表达式中读取一个字符
            if (isOperator(ch)) {        //判断是不是运算符————辅助函数，文章前面有写
                list.add(ch);        //将运算符加入list
                index++;            //index后移一位
            } else {  //如果是数字,就判断下一个是不是数字，如果是就进行组合（循环），然后录入list
                do {
                    ch = expression.substring(index, index + 1);    //读取一个字符
                    str.append(ch);              //拼接
                    index++;                //指针后移
                    //不是最后一个字符，并且下一个还是数字或者小数点，就进行循环
                } while ((index < expression.length()) && ((expression.charAt(index) <= 57 && expression.charAt(index) >= 48) || '.' == expression.charAt(index)));
                list.add(str.toString());            //将拼接的字符串录入到list
                str = new StringBuilder();                 //这里需要将str置位初值
            }
        } while (index < expression.length());        //限制条件，读到表达式最后一位就停止
        return list;                    //返回list
    }

    //传入前面得到的中缀表达式的list获取后缀表达式
    private static List<String> reversalOrder(List<String> tempList) {
        ArrayList<String> list = new ArrayList<>();                //创建一个list，用来存储中间数据
        Stack<String> stack = new Stack<>();            //创建一个栈，用于存储运算符
        for (String str : tempList) {                    //增强for循环，遍历集合中的数据
            if (str.matches("\\d+") || str.matches("\\d+\\.\\d+")) {    //判断是数，或者是小数
                list.add(str);                           //将数据录入list

            } else {                                     //遇到运算符
                if ("(".equals(str)) {                   //如果是"("，就压入栈中
                    stack.push(str);
                } else if (")".equals(str)) {            //如果遇到")"，就依次弹出栈中的运算符到list中，遇到"("才停止，然后丢弃这一对括号
                    //遇到左括号
                    while (!"(".equals(stack.peek())) {
                        list.add(stack.pop());            //将中间得到的运算符都录入到list中
                    }
                    stack.pop();                            //丢弃"("

                } else {                                    //遇到的是运算符
                    while (true) {
                        if (stack.isEmpty() || "(".equals(stack.peek())) {        //判断栈是不是空，或者栈中是左括号。如果满足一个，就说明是目前栈中的第一个运算符
                            stack.push(str);                //运算符直接入栈
                            break;                          //出循环，遍历下一个元素
                        } else if (getPriority(stack.peek()) < getPriority(str)) {
                            //如果运算符的优先级比栈顶运算符优先级高，就压入栈——辅助函数，文章前面有提供
                            stack.push(str);                //入栈
                            break;                          //出循环，遍历下一个元素
                        } else {
                            //和栈顶运算符相比优先级相等或者小于，就将栈顶运算符弹出并录入list中，然后该运算符就和栈中新的栈顶元素相比较（循环）
                            list.add(stack.pop());
                            //调用pop之后，下次循环自然就是新的栈顶元素
                        }
                    }
                }
            }
        }
        //将栈中的所有运算符全部弹出到list中
        while (!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;            //返回后缀表达式
    }

    public static double calculator(List<String> arr) {
        //从左到右，遇到数字就入栈，遇到运算符就弹出两个数据，然后：后弹出的-先弹出的，再将结果入栈
        Stack<String> stack = new Stack<String>();    //创建栈，用于存储数字
        double num1;                            //用于接收弹出的数字
        double num2;                            //用于接收弹出的数字
        String ret;                             //用于接收运算符
        for (String str : arr) {                //遍历list中数据
            if (str.matches("\\d+") || str.matches("\\d+\\.\\d+")) {    //如果是数字
                stack.push(str);        //入栈
            } else {
                //如果是运算符就弹出两个数据，并做运算，然后再压入栈中
                num1 = Double.parseDouble(stack.pop());
                num2 = Double.parseDouble(stack.pop());
                ret = operation(num1, num2, str) + "";        //返回类型为double，将其转化为String类型，因为是<String>栈
                stack.push(ret);
            }
        }
        //最后栈中剩一个元素，就是结果
        return Double.parseDouble(stack.pop());    //最后一步要记得类型转换
    }

    public static void main(String[] args) {
        String myEquation = "1.2-5+6*8-(4-1)";
        List<String> strEquation = arraysOrder(myEquation);
        List<String> reversalEquation = reversalOrder(strEquation);
        Double result = calculator(reversalEquation);
        System.out.println("后缀表达式为：" + reversalEquation);
        System.out.println("结果为：" + result);
    }
}